---------Mystery Double Feature--------
A 4am crack                  2016-11-09
---------------------------------------

Name: Mystery Double Feature
Genre: adventure
Year: 1984
Publisher: Scholastic, Inc.
Platform: Apple ][+ or later
Media: double-sided 5.25-inch floppy
OS: custom
Previous cracks: none
Similar cracks:
  #894 Number Stumper
  #840 Jumpman
  #595 Addition Magician
  #476 Microzine 2

Only side A is bootable. Side B just
says "Take the disk out and turn it
over. Press RETURN when ready."

I'm ready.

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  immediate disk read error

Locksmith Fast Disk Backup
  unable to read any track

EDD 4 bit copy (no sync, no count)
  read error on T22
  copy displays a graphical title page
    then hangs with the drive motor on

Copy ][+ nibble editor
  T00 -> standard prologues, modified
    epilogues (FF FF FF)
  T01,02 -> not full tracks? looks
    like they have some standard-ish
    sectors, but not 16 per track
    (also corrupted address fields)
  T03 -> corrupted address fields
    that claim to be track $00
  T04-T20 -> uncorrupted address fields
    but still non-standard epilogues
  T21-T22 unformatted (hi-res disk scan
    confirms this)

When I say "corrupted address fields,"
this is what that looks like:

                 --v--

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK: 03  START: 21DC  LENGTH: 189D
       ^^

21B8: FF FF FF FF FF FF FF FF   VIEW
21C0: FF FF FF FF FF FF FF FF
21C8: FF FF FF FF FF FF FF FF
21D0: FF FF FF FF FF FF FF FF
21D8: FF FF FF FF FF D5 AA 96  <-21DD
                     ^^^^^^^^
                 address prologue

21E0: AA AA AA AA AA AA AA AA
      ^^^^^ ^^^^^ ^^^^^ ^^^^^
      V000   T00   S00  chksm

21E8: FF FF FF FF FF CF F3 FC
      ^^^^^^^^
  address epilogue

21F0: FF FF D5 AA AD 9B DB B9
            ^^^^^^^^
         data prologue

21F8: B9 DB F2 DE B9 AE B3 BA

---------------------------------------

  A  TO ANALYZE DATA  ESC TO QUIT

  ?  FOR HELP SCREEN  /  CHANGE PARMS

  Q  FOR NEXT TRACK   SPACE TO RE-READ

                 --^--

The disk is lying to me. The address
field claims to be track $00, but it's
really track $03. Bad disk! Stop lying!

Disk Fixer
  ["O" -> "Input/Output Control"]
    set Address Epilogue to "FF FF FF"
    set Data Epilogue to "FF FF FF"
  T00 readable, disk volume is 000 (!)
  T01-T03 unreadable (no option to
    ignore the corrupted address field)
  T04-T20 readable, disk volume is 254
  T21+ unreadable (unformatted)

Copy ][+ sector editor
  ["P" -> "Sector Editor Patcher"]
    set type to "CUSTOM"
    set Address Epilogue to "FF FF"
    set Data Epilogue to "FF FF FF"
  T00 readable
  T04-T20 readable

  ["P" -> "Sector Editor Patcher"]
    set CHECK TRACK to "NO"
  only parts of T01 and T02 readable:
    T01: S03,04,05,06,07,0A,0B,0C,0D,0E
    T02: S01,02,08,09,0F
  T03 readable!
  T04-T20 readable!

Why didn't COPYA work?
  modified epilogue bytes on track $00
  (it never even got to the fun part)

Why didn't Locksmith FDB work?
  ditto

Why didn't my EDD copy work?
  I've seen similar disks, where the
  first N tracks have intentionally
  corrupted address fields. (N varies
  from disk to disk.) There's a custom
  loader that loads the data from those
  corrupted tracks, then transfers
  control to a standard RWTS for the
  rest of the program. Somewhere in the
  corrupted tracks, it will load data
  from consecutive half tracks. (These
  are devilishly difficult to copy, and
  I didn't even try.) That's just an
  educated guess; I could be surprised.

Hey, I can actually validate that guess
in the Copy ][+ nibble editor, which
can read half tracks.

                 --v--

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK: 01.50  START: 3700  LENGTH: 198E
       ^^^^^

3BD0: FF FF FF FF FF FF FF FF   VIEW
3BD8: FF FF FF FF FF FF FF FF
3BE0: FF FF FF FF FF FF FF FF
3BE8: FF FF C9 FF FF FF FF FF
3BF0: FF FF FF FF FF D5 AA 96  <-3BF5
                     ^^^^^^^^
                  address prologue

3BF8: AA AA AA AA AF AB AF AB
      ^^^^^ ^^^^^ ^^^^^ ^^^^^
      V000   T00   S0B  chksm

3C00: FF FF FF 9F E7 F9 FE FF
      ^^^^^^^^
  address eplogue

3C08: D5 AA AD A7 B4 BD CD ED
      ^^^^^^^^
   data prologue

3C10: ED 9B ED F2 E9 DF B6 AB

---------------------------------------

  A  TO ANALYZE DATA  ESC TO QUIT

  ?  FOR HELP SCREEN  /  CHANGE PARMS

  Q  FOR NEXT TRACK   SPACE TO RE-READ

                 --^--

Jackpot! (Note that it's still claiming
to be track $00, though, just like the
other whole tracks above and below it.)

Next steps:

  1. Trace the boot
  2. ???

                   ~

               Chapter 1
      In Which We Brag About Our
           Humble Beginnings


I have two floppy drives, one in slot 6
and the other in slot 5. My "work disk"
(in slot 5) runs Diversi-DOS 64K, which
is compatible with Apple DOS 3.3 but
relocates most of DOS to the language
card on boot. This frees up most of
main memory (only using a single page
at $BF00..$BFFF), which is useful for
loading large files or examining code
that lives in areas typically reserved
for DOS.

[S6,D1=original disk]
[S5,D1=my work disk]

The floppy drive firmware code at $C600
is responsible for aligning the drive
head and reading sector 0 of track 0
into main memory at $0800. Because the
drive can be connected to any slot, the
firmware code can't assume it's loaded
at $C600. If the floppy drive card were
removed from slot 6 and reinstalled in
slot 5, the firmware code would load at
$C500 instead.

To accommodate this, the firmware does
some fancy stack manipulation to detect
where it is in memory (which is a neat
trick, since the 6502 program counter
is not generally accessible). However,
due to space constraints, the detection
code only cares about the lower 4 bits
of the high byte of its own address.

Stay with me, this is all about to come
together and go boom.

$C600 (or $C500, or anywhere in $Cx00)
is read-only memory. I can't change it,
which means I can't stop it from
transferring control to the boot sector
of the disk once it's in memory. BUT!
The disk firmware code works unmodified
at any address. Any address that ends
with $x600 will boot slot 6, including
$B600, $A600, $9600, &c.

; copy drive firmware to $9600
*9600<C600.C6FFM

; and execute it
*9600G
...reboots slot 6, loads game...

Now then:

]PR#5
...
]CALL -151

*9600<C600.C6FFM

*96F8L

96F8-   4C 01 08    JMP   $0801

That's where the disk controller ROM
code ends and the on-disk code begins.
But $9600 is part of read/write memory.
I can change it at will. So I can
interrupt the boot process after the
drive firmware loads the boot sector
from the disk but before it transfers
control to the disk's bootloader.

; instead of jumping to on-disk code,
; copy boot sector to higher memory so
; it survives a reboot
96F8-   A0 00       LDY   #$00
96FA-   B9 00 08    LDA   $0800,Y
96FD-   99 00 28    STA   $2800,Y
9700-   C8          INY
9701-   D0 F7       BNE   $96FA

; turn off slot 6 drive motor
9703-   AD E8 C0    LDA   $C0E8

; reboot to my work disk in slot 5
9706-   4C 00 C5    JMP   $C500

*BSAVE TRACE,A$9600,L$109
*9600G
...reboots slot 6...
...reboots slot 5...

]BSAVE BOOT0,A$2800,L$100

Now let's see how this disk boots.

                   ~

               Chapter 2
         Boot Trace and Chill


]CALL -151

; move boot0 back into place
*800<2800.28FFM

*801L

; set reset vector
0801-   8A          TXA
0802-   4A          LSR
0803-   4A          LSR
0804-   4A          LSR
0805-   4A          LSR
0806-   09 C0       ORA   #$C0
0808-   85 3F       STA   $3F
080A-   8D F3 03    STA   $03F3
080D-   49 A5       EOR   #$A5
080F-   8D F4 03    STA   $03F4
0812-   A9 00       LDA   #$00
0814-   8D F2 03    STA   $03F2

; hmm
0817-   A9 04       LDA   #$04
0819-   48          PHA

; machine initialization (memory banks,
; TEXT, IN#0, PR#0, HOME, &c.)
081A-   8D 81 C0    STA   $C081
081D-   20 2F FB    JSR   $FB2F
0820-   8D 52 C0    STA   $C052
0823-   20 89 FE    JSR   $FE89
0826-   20 93 FE    JSR   $FE93
0829-   20 58 FC    JSR   $FC58
082C-   8D 51 C0    STA   $C051
082F-   8D 54 C0    STA   $C054
0832-   8D 52 C0    STA   $C052

; set up ($3E) vector to point to the
; sector read routine in the disk
; controller ROM
0835-   A9 5C       LDA   #$5C
0837-   85 3E       STA   $3E

; the disk controller ROM always exits
; via $0801, so set that to an RTS so
; we can JSR and not have to set up a
; loop
0839-   A9 60       LDA   #$60
083B-   8D 01 08    STA   $0801

; hmm again
083E-   A9 72       LDA   #$72
0840-   48          PHA

OK, we've now pushed $04/$72 on the
stack. That's probably important.

; multi-sector read
; Y = first logical sector ($01)
; X = last logical sector ($09)
; A = start address high byte ($09)
0841-   A0 00       LDY   #$00
0843-   84 FC       STY   $FC
0845-   C8          INY
0846-   A9 09       LDA   #$09
0848-   A2 09       LDX   #$09

; call multi-sector read routine
084A-   20 77 08    JSR   $0877

; call the code we just read (probably
; displays the "Scholastic WizWare"
; title screen)
084D-   20 00 09    JSR   $0900

; save part of text page
0850-   A0 00       LDY   #$00
0852-   B9 00 04    LDA   $0400,Y
0855-   99 00 80    STA   $8000,Y
0858-   88          DEY
0859-   D0 F7       BNE   $0852
085B-   B9 00 05    LDA   $0500,Y
085E-   99 00 81    STA   $8100,Y
0861-   88          DEY
0862-   D0 F7       BNE   $085B

; another sector read, three sectors
; into $9D00..$9FFF
0864-   A9 9D       LDA   #$9D
0866-   A0 0A       LDY   #$0A
0868-   A2 0C       LDX   #$0C
086A-   20 77 08    JSR   $0877

; another sector read, this time just
; one sector, into $B00 (X is already
; less than Y on entry, so loop will
; exit after one read)
086D-   A9 B0       LDA   #$B0
086F-   A2 00       LDX   #$00
0871-   20 77 08    JSR   $0877

; another sector read, again just one
; sector, into $0400
0874-   A9 04       LDA   #$04
0876-   AA          TAX

; falls through to multi-sector read
; entry point (was also called earlier)
0877-   85 27       STA   $27
0879-   E8          INX
087A-   86 49       STX   $49
087C-   84 F9       STY   $F9

; map logical into physical sector and
; store it in zero page where the disk
; controller ROM will look for it
087E-   B9 95 08    LDA   $0895,Y
0881-   85 3D       STA   $3D

; read sector via disk controller ROM
0883-   20 90 08    JSR   $0890

; loop until done
0886-   A4 F9       LDY   $F9
0888-   C8          INY
0889-   C4 49       CPY   $49
088B-   90 EF       BCC   $087C
088D-   A5 27       LDA   $27
088F-   60          RTS

; subroutine to read a sector via ($3E)
; which points to $Cx5C, which exits
; via $0801, which is now an "RTS"
; (HOW F---ING ELEGANT IS THAT, RIGHT?)
0890-   A6 2B       LDX   $2B
0892-   6C 3E 00    JMP   ($003E)

; physical to logical sector map

0895- .. .. .. .. .. 00 03 05
0898- 07 09 0B 0D 0F 02 04 06
08A0- 08 0A 0C 0E 01

That's it. Flexible but compact.

It's a weird combination of reads,
though. It loads a bunch of sectors at
$0900, then 3 @ $9D00, 1 @ $B000, and
the last one at $0400 (!). That's part
of the text page, but it's hidden
during boot because the subroutine at
$900 switches to the hi-res graphics
page. (Not shown, but trust me.)

Of course, we manually pushed $04/$72
on the stack earlier, so once we fall
through to the sector read routine,
read the last sector, and hit the RTS
we put at $0801, we will "return" to
$0472 + 1 = $0473.

Let's interrupt the boot before it gets
there.

                   ~

               Chapter 3
 In Which Things Get Brilliantly Weird


*9600<C600.C6FFM

; set up callback by changing the two
; bytes that are pushed to the stack
96F8-   A9 97       LDA   #$97
96FA-   8D 18 08    STA   $0818
96FD-   A9 04       LDA   #$04
96FF-   8D 3F 08    STA   $083F

; start the boot
9702-   4C 01 08    JMP   $0801

; (callback is here)
; copy $0400 up to higher memory so it
; survives a reboot
9705-   A0 00       LDY   #$00
9707-   B9 00 04    LDA   $0400,Y
970A-   99 00 24    STA   $2400,Y
970D-   C8          INY
970E-   D0 F7       BNE   $9707

; turn off slot 6 drive motor
9710-   AD E8 C0    LDA   $C0E8

; reboot to my work disk
9713-   4C 00 C5    JMP   $C500

*BSAVE TRACE2,A$9600,L$116
*9600G
...reboots slot 6...
...reboots slot 5...

]BSAVE BOOT1,A$2400,L$100
]CALL -151

The entry point was $0473, so let's
start there. I'll have to leave the
code at $2400. Relative branches will
look correct, but absolute addresses
in $04xx will be +$2000.

*2473L

; zp$4A is important later (see below)
2473-   46 4A       LSR   $4A
2475-   20 91 04    JSR   $0491

*2491L

2491-   20 33 04    JSR   $0433

*2433L

; call the following line (then fall
; through and do it again)
2433-   20 36 04    JSR   $0436

; save A and Y
2436-   48          PHA
2437-   98          TYA
2438-   48          PHA

; low-level disk stuff (see below)
2439-   A5 FC       LDA   $FC
243B-   85 FD       STA   $FD
243D-   E6 FC       INC   $FC
243F-   A5 FC       LDA   $FC
2441-   29 03       AND   #$03
2443-   0A          ASL
2444-   05 2B       ORA   $2B
2446-   A8          TAY
2447-   B9 81 C0    LDA   $C081,Y

; wait loop
244A-   A9 30       LDA   #$30
244C-   20 A8 FC    JSR   $FCA8

; more low-level disk stuff
244F-   A5 FD       LDA   $FD
2451-   29 03       AND   #$03
2453-   0A          ASL
2454-   05 2B       ORA   $2B
2456-   A8          TAY
2457-   B9 80 C0    LDA   $C080,Y

; more waiting
245A-   A9 30       LDA   #$30
245C-   20 A8 FC    JSR   $FCA8

; restore A and Y on the way out
245F-   68          PLA
2460-   A8          TAY
2461-   68          PLA
2462-   60          RTS

This is a very clever and compact way
to advance the drive head to the next
track. Normally DOS 3.3 keeps track of
this and has a (much more complicated)
routine to move the head back and forth
as needed. But this loader only needs
to move it forward, so the entire
process collapses to this:

1. Set up the Y register to be a slot
   number (x16) plus the appropriate
   phase (0-3, depending on which track
   the drive head is on)

2. LDA $C081,Y to turn on the
   appropriate stepper motor

3. Wait exactly the right amount of
   time (as measured in CPU cycles)

4. LDA $C080,Y to turn off the
   appropriate stepper motor

5. Wait the right amount of time again

...which is exactly what this routine
at $0436 is doing. But that only gets
us halfway there -- literally, it only
moves the drive head by half a track.
But! Since $0433 "falls through" to
$0436, it ends up doing this twice. Two
half tracks equal one whole track, so
calling the routine at $0433 will move
the drive head to the next whole track.

So now we're on track 1.

(By the way, this is why it initialized
zero page $FC to $00 at $0843. That's
the "current" track where the drive
head is at boot; it gets updated when
the drive head advances.)

Everything I know about low-level disk
stepping, I learned from this excellent
Usenet post:
macgui.com/usenet/?group=1&id=31160

                   ~

               Chapter 4
         Spiraling, Spiraling,
       Spiraling Towards Freedom


Continuing from $0494...

; zero page fiddling (see below)
2494-   A9 00       LDA   #$00
2496-   85 41       STA   $41
2498-   38          SEC
2499-   66 4A       ROR   $4A

; multi-sector read, similar to the one
; we did in boot0
; Y = first logical sector ($01)
; X = last logical sector ($05)
; A = start address high byte ($B1)
249B-   A9 B1       LDA   #$B1
249D-   A0 01       LDY   #$01
249F-   A2 05       LDX   #$05
24A1-   20 15 04    JSR   $0415

; move the drive head one phase only,
; to the next HALF track
24A4-   20 36 04    JSR   $0436

[now on track 1.5]

; read more sectors ($06..$0A)
24A7-   A2 0A       LDX   #$0A
24A9-   20 15 04    JSR   $0415

; advance another half track
24AC-   20 36 04    JSR   $0436

[now on track 2]

; read more sectors ($0B..$0F)
24AF-   A2 0F       LDX   #$0F
24B1-   20 15 04    JSR   $0415

; fiddle with $4A again
24B4-   46 4A       LSR   $4A
24B6-   60          RTS

So here's the deal with $4A: we
initialized it at $0473 by a blind LSR,
which clears the high bit. This tells
the multi-sector read routine at $0415
to use logical sectors. Then we set the
high bit at $0498 with SEC + ROR,
indicating we want $0415 to read
physical sectors. Then we read a few
sectors from track 1, a few from track
1.5, and a few from track 2. Then we
reset $4A with another LSR, and we're
back to using logical sectors.

This explains why my EDD bit copy
failed. This disk is storing data on
half tracks. Worse, it's storing data
on *adjacent* half tracks -- a few on
track 1, a few on track 1.5, and a few
on track 2. Due to limitations of the
Disk II drive mechanism, that would be
virtually impossible for a generic bit
copier to reproduce.

Every part of this code is brilliant,
AND it fits in a single sector on the
text page, AND it's flexible enough to
read from virtually uncopyable disks.

Popping the stack and continuing from
the caller at $0478...

2478-   A9 A0       LDA   #$A0
247A-   20 0E 04    JSR   $040E

$040E is yet another flexible entry
point. It calls $0433 to advance a
whole track, then falls through to
$0411 to set Y=$00 and X=$0F, then
falls through to $0415 to read multiple
sectors -- in other words, an entire
track.

So we're reading track 3 into $A000..
$AFFF.

We can capture all of this -- the 15
sectors at $B100 and the 16 sectors
at $A000 -- in one shot.

*9600<C600.C6FFM

; set up callback #1
96F8-   A9 97       LDA   #$97
96FA-   8D 18 08    STA   $0818
96FD-   A9 04       LDA   #$04
96FF-   8D 3F 08    STA   $083F

; start the boot
9702-   4C 01 08    JMP   $0801

; (callback #1 is here)
; set up callback #2
9705-   A9 4C       LDA   #$4C
9707-   8D 7D 04    STA   $047D
970A-   A9 17       LDA   #$17
970C-   8D 7E 04    STA   $047E
970F-   A9 97       LDA   #$97
9711-   8D 7F 04    STA   $047F

; continue the boot
9714-   4C 73 04    JMP   $0473

; (callback #2 is here)
; save what appears to be an entire
; copy of DOS 3.3 in lower memory so it
; survives a reboot
9717-   A2 23       LDX   #$23
9719-   A0 00       LDY   #$00
971B-   B9 00 9D    LDA   $9D00,Y
971E-   99 00 2D    STA   $2D00,Y
9721-   C8          INY
9722-   D0 F7       BNE   $971B
9724-   EE 1D 97    INC   $971D
9727-   EE 20 97    INC   $9720
972A-   CA          DEX
972B-   D0 EE       BNE   $971B

; turn off slot 6 drive motor and
; reboot to my work disk
972D-   AD E8 C0    LDA   $C0E8
9730-   4C 00 C5    JMP   $C500

*BSAVE TRACE3,A$9600,L$133
*9600G
...reboots slot 6...
...reboots slot 5...

]BSAVE OBJ.9D00-BFFF,A$2D00,L$2300

                   ~

               Chapter 5
       In Which We See The Light
       At The End Of The Tunnel,
       And It's A DOS-shaped RWTS
       Which Is A Weird Thing To
       See In A Tunnel, Honestly


Continuing from $047D...

]BLOAD BOOT1
]CALL -151

*247DL

; set up DOS globals (tracking where
; the drive head is)
247D-   A6 2B       LDX   $2B
247F-   8E E9 B7    STX   $B7E9
2482-   20 8E BE    JSR   $BE8E
2485-   A5 FC       LDA   $FC
2487-   99 78 04    STA   $0478,Y
248A-   4A          LSR
248B-   8D 78 04    STA   $0478

; and continue elsewhere
248E-   4C 02 B7    JMP   $B702

Hey, that's still in memory! It's off
by a bit. Let's see... $B702 would be
at $4702.

*4702L

4702-   20 00 BB    JSR   $BB00

*4B00L

; restore the text page that we saved
; earlier (way back at $0850)
4B00-   B9 78 04    LDA   $0478,Y
4B03-   48          PHA
4B04-   A2 00       LDX   #$00
4B06-   BD 00 80    LDA   $8000,X
4B09-   9D 00 04    STA   $0400,X
4B0C-   CA          DEX
4B0D-   D0 F7       BNE   $4B06
4B0F-   BD 00 81    LDA   $8100,X
4B12-   9D 00 05    STA   $0500,X
4B15-   CA          DEX
4B16-   D0 F7       BNE   $4B0F
4B18-   68          PLA
4B19-   99 78 04    STA   $0478,Y
4B1C-   60          RTS

Continuing from $B705...

4705-   A5 3F       LDA   $3F
4707-   8D 01 B7    STA   $B701
470A-   EA          NOP
470B-   EA          NOP
470C-   EA          NOP
470D-   EA          NOP
470E-   4C 3B B7    JMP   $B73B

*473BL

473B-   A2 FF       LDX   #$FF
473D-   9A          TXS
473E-   8E EB B7    STX   $B7EB
4741-   20 69 BA    JSR   $BA69
4744-   20 89 FE    JSR   $FE89
4747-   4C 84 9D    JMP   $9D84

Well that looks suspiciously like a
late-stage DOS boot. In fact, we now
have a lot of clues that -- despite the
craziness -- we have in fact loaded a
full copy of DOS into the usual place
at the top of writeable main memory
($9D00..$BFFF).

*4800L
.
. appears to be a DOS-shaped RWTS
.
; standard routine to find the address
; prologue ("D5 AA 96")
4944-   A0 FC       LDY   #$FC
4946-   84 26       STY   $26
4948-   C8          INY
4949-   D0 04       BNE   $494F
494B-   E6 26       INC   $26
494D-   F0 F3       BEQ   $4942
494F-   BD 8C C0    LDA   $C08C,X
4952-   10 FB       BPL   $494F
4954-   C9 D5       CMP   #$D5
4956-   D0 F0       BNE   $4948
4958-   EA          NOP
4959-   BD 8C C0    LDA   $C08C,X
495C-   10 FB       BPL   $4959
495E-   C9 AA       CMP   #$AA
4960-   D0 F2       BNE   $4954
4962-   A0 03       LDY   #$03
4964-   BD 8C C0    LDA   $C08C,X
4967-   10 FB       BPL   $4964
4969-   C9 96       CMP   #$96
496B-   D0 E7       BNE   $4954
.
.
.
; non-standard epilogue bytes (FF FF)
498B-   BD 8C C0    LDA   $C08C,X
498E-   10 FB       BPL   $498B
4990-   C9 FF       CMP   #$FF
4992-   D0 AE       BNE   $4942
4994-   EA          NOP
4995-   BD 8C C0    LDA   $C08C,X
4998-   10 FB       BPL   $4995
499A-   C9 FF       CMP   #$FF
499C-   D0 A4       BNE   $4942
499E-   18          CLC
499F-   60          RTS

Confirmed: after the bootloader exits,
we have a full DOS 3.3 in memory. Spot
checking the RWTS, it's perfectly
normal except it expects "FF FF FF"
epilogue bytes. Which, by the way, is
just the sort of RWTS that could read
tracks $04-$20.

                   ~

               Chapter 6
       Do You Know Demuffin Man,
       Demuffin Man, Demuffin Man?
       Do You Know Demuffin Man
       Who Lives On Floppy Lane?


Advanced Demuffin (written in 1983 by
The Stack, updated in 2014 by yours
truly) is a wonderful automation tool
that uses a copy protected disk against
itself. It literally calls the original
RWTS to read the original disk, then
using a standard RWTS to write out the
sector data to another disk, but in a
standard format. You just supply the
original disk's RWTS and choose which
tracks and sectors to convert, and it
does the rest.

As it happens, I already have the
original RWTS in memory (though I'll
move it into place later).

[S5,D1=my work disk (still)]
[S6,D1=original disk (still)]
[S6,D2=blank disk]

]BRUN ADVANCED DEMUFFIN 1.5

["X" to exit to monitor]

*B800<4800.4FFFM ; move RWTS into place

*800G            ; resume Advanced DM

["C" to convert disk]

["Y" to change default values]

                 --v--

ADVANCED DEMUFFIN 1.5    (C) 1983, 2014
ORIGINAL BY THE STACK    UPDATES BY 4AM
=======================================


INPUT ALL VALUES IN HEX


SECTORS PER TRACK? (13/16) 16

START TRACK: $04        <-- change this
START SECTOR: $00
END TRACK: $20          <-- change this
END SECTOR: $0F

INCREMENT: 1

MAX # OF RETRIES: 0

COPY FROM DRIVE 1
TO DRIVE: 2
=======================================
16SC $07,$00-$18,$0F BY1.0 S6,D1->S6,D2

                 --^--

And here we go...

                 --v--

ADVANCED DEMUFFIN 1.5    (C) 1983, 2014
ORIGINAL BY THE STACK    UPDATES BY 4AM
=======PRESS ANY KEY TO CONTINUE=======
TRK:    .............................
+.5:
    0123456789ABCDEF0123456789ABCDEF012
SC0:    .............................
SC1:    .............................
SC2:    .............................
SC3:    .............................
SC4:    .............................
SC5:    .............................
SC6:    .............................
SC7:    .............................
SC8:    .............................
SC9:    .............................
SCA:    .............................
SCB:    .............................
SCC:    .............................
SCD:    .............................
SCE:    .............................
SCF:    .............................
=======================================
16SC $04,$00-$20,$0F BY1.0 S6,D1->S6,D2

                 --^--

This is the power and the genius of
Advanced Demuffin. Every disk must be
able to read itself. So, let it read
itself, then capture the data and write
it out in a standard format.

I also copied track 0 the same way, by
using Advanced Demuffin to read the
original disk and write out the track
to write it out to the disk in S6,D2.
Now I have all tracks converted that
can be easily converted.

                   ~

              Chapter 7
     In Which Everything Is Simple
    If You Look At It The Right Way


Now I get to write everything that I
captured from the corrupted tracks back
to tracks $01-$03.

Here is the original disk layout:

track | sectors | address
------+---------+-------------
  1   | $01-$05 | $B100-$B5FF
 1.5  | $06-$0A | $B600-$BAFF
  2   | $0B-$0F | $BB00-$BFFF
  3   | $00-$0F | $A000-$AFFF

And here is the new layout:

track | sectors | address
------+---------+-------------
  1   | $00-$0F | $A000-$AFFF
  2   | $00-$0F | $B000-$BFFF
  3   |      unused

Of course, all the tracks will now have
normal address fields (no more lying
about the track numbers). Also no half
tracks and no spirals. Just, you know,
sectors on a disk. All the spiral track
stuff collapses into a single track, so
track $03 is completely unused.

[S6,D1=demuffin'd copy (T00, T04-T20)]
[S5,D1=my work disk]

]PR#5
...
]CALL -151

*300L

; page count (decremented)
0300-   A9 20       LDA   #$20
0302-   85 FF       STA   $FF

; logical sector (incremented)
0304-   A9 00       LDA   #$00
0306-   85 FE       STA   $FE

; call RWTS to write sector
0308-   A9 03       LDA   #$03
030A-   A0 88       LDY   #$88
030C-   20 D9 03    JSR   $03D9

; increment logical sector, wrap around
; from $0F to $00 and increment track
030F-   E6 FE       INC   $FE
0311-   A4 FE       LDY   $FE
0313-   C0 10       CPY   #$10
0315-   D0 07       BNE   $031E
0317-   A0 00       LDY   #$00
0319-   84 FE       STY   $FE
031B-   EE 8C 03    INC   $038C

; Convert to the interleave order that
; this disk expects
031E-   B9 40 03    LDA   $0340,Y
0321-   8D 8D 03    STA   $038D

; increment page to write
0324-   EE 91 03    INC   $0391
0327-   C6 FF       DEC   $FF

; loop until done with all pages
0329-   D0 DD       BNE   $0308
032B-   60          RTS

; sector interleave table
*340.34F

0340- 00 06 05 04 03 02 01 0F
0348- 0E 0D 0C 0B 0A 09 08 07

; RWTS parameter table, pre-initialized
; with slot 6, drive 1, track $01,
; sector $00, address $1000, and RWTS
; write command ($02)
*388.397

0388- 01 60 01 00 01 00 FB F7
0390- 00 10 00 00 02 00 00 60

*BSAVE MAKE,A$300,L$98

; clear memory
*800:00 N 801<800.7FFEM

*BLOAD OBJ.9D00-BFFF,A$D00

Now $A000..$BFFF is at $1000..$2FFF,
ready to be written out to tracks 1 and
2 on my copy.

; patch the RWTS to read and write from
; standard disks (using the standard
; epilogue instead of FF FF)
*289E:DE
*28A3:AA
*2935:DE
*293F:AA
*2991:DE
*299A:AA
*2CAE:DE
*2CB3:AA

*300G
...write write write...

Now I need to modify the bootloader at
$0473 slightly.

  1. Modify the routine at $0433 (that
     advances the drive head) so it
     updates zero page $41 with the
     current track.

  2. Modify the calls at $0473 so it
     reads $A000..$BFFF from tracks 1
     and 2. No tricks, no traps, no
     spirals. (I know we read $B000
     from track 0, but that's OK. It's
     the same data, and this is a
     minimally invasive patch.)

The sector read routine at $C65C
compares the track listed in the
address field to zero page $41 and
loops forever until it matches. $C600
initializes $41 to 0, and the original
disk never updates $41, but everything
works because the address fields are
corrupted and all claim to be track 0.

So part of it will be simpler, because
we'll no longer be spiraling between
tracks. But part of it will actually
be more complicated because the address
fields are no longer corrupted and we
need to track the track number.

First, a new routine at $0491 (now
unused space) to increment the track
number in zero page.

T00,S08,$91 -> E6 41 4C 36 04

                 --v--

0091:E6 41          INC   $41
0093:4C 36 04       JMP   $0436

                 --^--

Second, a patch at $0433 to call the
new routine at $0491 so we increment
the track number in zero page before
advancing the drive arm.

T00,S08,$34: 36 -> 91

                 --v--

0033:20 91 04       JSR   $0491

                 --^--

Third, patches at $0473 to skip the
spiral tracking subroutine altogether
and instead load tracks 1 and 2 into
$A000..$BFFF in one shot.

; skip spiral tracking subroutine
T00,S08,$75: 20 -> 2C

; read two tracks instead of one into
; $A000
T00,S08,$7B: 0E -> 0B

                 --v--

0073:46 4A          LSR   $4A
0075:2C 91 04       BIT   $0491
0078:A9 A0          LDA   #$A0
007A:20 0B 04       JSR   $040B

                 --^--

($040B calls $040E to advance to the
next track and read it, then falls
through to... $040E to do it again and
store it in consecutive memory. How
elegant is that?!)

]PR#6
...works, and it is glorious...

Side B uses the same non-standard
epilogues as side A, so I was able to
convert it with the same procedure.
Tracks $21 and $22 are once again
unforamtted, along with track $01 for
no reason except to make my life more
difficult.

Quod erat liberandum.

---------------------------------------
A 4am crack                     No. 897
------------------EOF------------------
